; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv64 -mattr=+d,+zbb,+zfh -verify-machineinstrs -mcpu=sifive-u74 \
; RUN: -O1 -riscv-enable-machine-combiner=true -riscv-force-machine-combiner-strategy=local < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK_LOCAL

; RUN: llc -mtriple=riscv64 -mattr=+d,+zbb,+zfh -verify-machineinstrs -mcpu=sifive-u74 \
; RUN: -O1 -riscv-enable-machine-combiner=true -riscv-force-machine-combiner-strategy=min-instr < %s | \
; RUN: FileCheck %s --check-prefixes=CHECK,CHECK_GLOBAL

define double @test_reassoc_fadd1(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa2, fa3
; CHECK-NEXT:    fadd.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fadd nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fadd2(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa2, fa3
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %a2, %t0
  %t2 = fadd nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fadd3(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa3, fa2
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fadd nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fadd4(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd4:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa3, fa2
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %a2, %t0
  %t2 = fadd nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fmul1(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fmul1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmul.d fa4, fa2, fa3
; CHECK-NEXT:    fmul.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fmul nsz reassoc double %a0, %a1
  %t1 = fmul nsz reassoc double %t0, %a2
  %t2 = fmul nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fmul2(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fmul2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmul.d fa4, fa2, fa3
; CHECK-NEXT:    fmul.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul nsz reassoc double %a0, %a1
  %t1 = fmul nsz reassoc double %a2, %t0
  %t2 = fmul nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fmul3(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fmul3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmul.d fa4, fa3, fa2
; CHECK-NEXT:    fmul.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul nsz reassoc double %a0, %a1
  %t1 = fmul nsz reassoc double %t0, %a2
  %t2 = fmul nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fmul4(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fmul4:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmul.d fa4, fa3, fa2
; CHECK-NEXT:    fmul.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul nsz reassoc double %a0, %a1
  %t1 = fmul nsz reassoc double %a2, %t0
  %t2 = fmul nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_big1(double %a0, double %a1, double %a2, double %a3, double %a4, double %a5, double %a6) {
; CHECK-LABEL: test_reassoc_big1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa1, fa0, fa1
; CHECK-NEXT:    fadd.d fa3, fa2, fa3
; CHECK-NEXT:    fadd.d fa5, fa4, fa5
; CHECK-NEXT:    fadd.d fa4, fa1, fa3
; CHECK-NEXT:    fadd.d fa5, fa5, fa6
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fadd nsz reassoc double %t1, %a3
  %t3 = fadd nsz reassoc double %t2, %a4
  %t4 = fadd nsz reassoc double %t3, %a5
  %t5 = fadd nsz reassoc double %t4, %a6
  ret double %t5
}

define double @test_reassoc_big2(double %a0, double %a1, i32 %a2, double %a3, i32 %a4, double %a5) {
; CHECK-LABEL: test_reassoc_big2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa3, fa2
; CHECK-NEXT:    fadd.d fa3, fa2, fa1
; CHECK-NEXT:    fcvt.d.w ft0, a0
; CHECK-NEXT:    fcvt.d.w ft1, a1
; CHECK-NEXT:    fmul.d fa2, fa2, ft0
; CHECK-NEXT:    fmul.d fa1, ft1, fa1
; CHECK-NEXT:    fsub.d fa5, fa4, fa5
; CHECK-NEXT:    fmul.d fa4, fa0, fa3
; CHECK-NEXT:    fmul.d fa3, fa1, fa2
; CHECK-NEXT:    fmul.d fa5, fa5, fa4
; CHECK-NEXT:    fmul.d fa0, fa5, fa3
; CHECK-NEXT:    ret
  %cvt1 = sitofp i32 %a2 to double
  %cvt2 = sitofp i32 %a4 to double
  %t5 = fmul nsz reassoc double %a3, %cvt1
  %t9 = fmul nsz reassoc double %cvt2, %t5
  %t4 = fmul nsz reassoc double %t9, %a1
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %a3, %t0
  %t3 = fadd nsz reassoc double %a3, %a1
  %t6 = fmul nsz reassoc double %t4, %a0
  %t2 = fsub nsz reassoc double %a5, %t1
  %t7 = fmul nsz reassoc double %t6, %t3
  %t8 = fmul nsz reassoc double %t2, %t7
  ret double %t8
}

; Negative test
define double @test_reassoc_fadd_flags_1(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd_flags_1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa5, fa5, fa2
; CHECK-NEXT:    fadd.d fa0, fa5, fa3
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd double %t0, %a2
  %t2 = fadd nsz reassoc double %t1, %a3
  ret double %t2
}

; Negative test
define double @test_reassoc_fadd_flags_2(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fadd_flags_2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa5, fa5, fa2
; CHECK-NEXT:    fadd.d fa0, fa5, fa3
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fadd double %t1, %a3
  ret double %t2
}

define double @test_fmadd1(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_fmadd1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmadd.d fa5, fa0, fa1, fa2
; CHECK-NEXT:    fmadd.d fa4, fa0, fa1, fa3
; CHECK-NEXT:    fadd.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fmul contract double %a0, %a1
  %t1 = fadd contract double %t0, %a2
  %t2 = fadd contract double %a3, %t0
  %t3 = fadd double %t1, %t2
  ret double %t3
}

define double @test_fmadd2(double %a0, double %a1, double %a2) {
; CHECK-LABEL: test_fmadd2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmadd.d fa4, fa0, fa1, fa2
; CHECK-NEXT:    fdiv.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul contract double %a0, %a1
  %t1 = fadd contract double %t0, %a2
  %t2 = fdiv double %t1, %t0
  ret double %t2
}

define double @test_fmsub(double %a0, double %a1, double %a2) {
; CHECK-LABEL: test_fmsub:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fmsub.d fa4, fa0, fa1, fa2
; CHECK-NEXT:    fdiv.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul contract double %a0, %a1
  %t1 = fsub contract double %t0, %a2
  %t2 = fdiv double %t1, %t0
  ret double %t2
}

define double @test_fnmsub(double %a0, double %a1, double %a2) {
; CHECK-LABEL: test_fnmsub:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmul.d fa5, fa0, fa1
; CHECK-NEXT:    fnmsub.d fa4, fa0, fa1, fa2
; CHECK-NEXT:    fdiv.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fmul contract double %a0, %a1
  %t1 = fsub contract double %a2, %t0
  %t2 = fdiv double %t1, %t0
  ret double %t2
}

define double @test_reassoc_fsub1(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa2, fa3
; CHECK-NEXT:    fadd.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fsub nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub2(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa2, fa3
; CHECK-NEXT:    fsub.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %t0, %a2
  %t2 = fadd nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub3(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa2, fa3
; CHECK-NEXT:    fsub.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %t0, %a2
  %t2 = fsub nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub4(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub4:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa2, fa3
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %a2, %t0
  %t2 = fsub nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub5(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub5:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa2, fa3
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %a2, %t0
  %t2 = fadd nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub6(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub6:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa2, fa3
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %a2, %t0
  %t2 = fsub nsz reassoc double %t1, %a3
  ret double %t2
}

define double @test_reassoc_fsub7(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub7:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa3, fa2
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %t0, %a2
  %t2 = fsub nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fsub8(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa3, fa2
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %t0, %a2
  %t2 = fadd nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fsub9(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub9:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa3, fa2
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %t0, %a2
  %t2 = fsub nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fsub10(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub10:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa3, fa2
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fadd nsz reassoc double %a2, %t0
  %t2 = fsub nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fsub11(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub11:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fadd.d fa4, fa3, fa2
; CHECK-NEXT:    fsub.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %a2, %t0
  %t2 = fadd nsz reassoc double %a3, %t1
  ret double %t2
}

define double @test_reassoc_fsub12(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_reassoc_fsub12:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fadd.d fa5, fa0, fa1
; CHECK-NEXT:    fsub.d fa4, fa3, fa2
; CHECK-NEXT:    fadd.d fa0, fa4, fa5
; CHECK-NEXT:    ret
  %t0 = fadd nsz reassoc double %a0, %a1
  %t1 = fsub nsz reassoc double %a2, %t0
  %t2 = fsub nsz reassoc double %a3, %t1
  ret double %t2
}

define i8 @test_reassoc_add_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_add_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    add a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i8 %a0, %a1
  %t1 = add i8 %t0, %a2
  %t2 = add i8 %t1, %a3
  ret i8 %t2
}

define i16 @test_reassoc_add_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_add_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    add a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i16 %a0, %a1
  %t1 = add i16 %t0, %a2
  %t2 = add i16 %t1, %a3
  ret i16 %t2
}

define i32 @test_reassoc_add_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_add_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    addw a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i32 %a0, %a1
  %t1 = add i32 %t0, %a2
  %t2 = add i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_add_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_add_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    add a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i64 %a0, %a1
  %t1 = add i64 %t0, %a2
  %t2 = add i64 %t1, %a3
  ret i64 %t2
}

define i32 @test_reassoc_add_sub_i32_1(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i32_1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    subw a2, a2, a3
; CHECK-NEXT:    subw a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i32 %a0, %a1
  %t1 = sub i32 %t0, %a2
  %t2 = add i32 %t1, %a3
  ret i32 %t2
}

define i32 @test_reassoc_add_sub_i32_2(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i32_2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    subw a2, a2, a3
; CHECK-NEXT:    addw a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i32 %a0, %a1
  %t1 = add i32 %t0, %a2
  %t2 = sub i32 %t1, %a3
  ret i32 %t2
}

define i32 @test_reassoc_add_sub_i32_3(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i32_3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    subw a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i32 %a0, %a1
  %t1 = sub i32 %t0, %a2
  %t2 = sub i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_add_sub_i64_1(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i64_1:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    sub a2, a2, a3
; CHECK-NEXT:    sub a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i64 %a0, %a1
  %t1 = sub i64 %t0, %a2
  %t2 = add i64 %t1, %a3
  ret i64 %t2
}

define i64 @test_reassoc_add_sub_i64_2(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i64_2:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    sub a2, a2, a3
; CHECK-NEXT:    add a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i64 %a0, %a1
  %t1 = add i64 %t0, %a2
  %t2 = sub i64 %t1, %a3
  ret i64 %t2
}

define i64 @test_reassoc_add_sub_i64_3(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_add_sub_i64_3:
; CHECK:       # %bb.0:
; CHECK-NEXT:    add a0, a0, a1
; CHECK-NEXT:    add a2, a2, a3
; CHECK-NEXT:    sub a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = add i64 %a0, %a1
  %t1 = sub i64 %t0, %a2
  %t2 = sub i64 %t1, %a3
  ret i64 %t2
}

define i8 @test_reassoc_and_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_and_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    and a0, a0, a1
; CHECK-NEXT:    and a2, a2, a3
; CHECK-NEXT:    and a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = and i8 %a0, %a1
  %t1 = and i8 %t0, %a2
  %t2 = and i8 %t1, %a3
  ret i8 %t2
}

define i16 @test_reassoc_and_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_and_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    and a0, a0, a1
; CHECK-NEXT:    and a2, a2, a3
; CHECK-NEXT:    and a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = and i16 %a0, %a1
  %t1 = and i16 %t0, %a2
  %t2 = and i16 %t1, %a3
  ret i16 %t2
}

define i32 @test_reassoc_and_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_and_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    and a0, a0, a1
; CHECK-NEXT:    and a2, a2, a3
; CHECK-NEXT:    and a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = and i32 %a0, %a1
  %t1 = and i32 %t0, %a2
  %t2 = and i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_and_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_and_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    and a0, a0, a1
; CHECK-NEXT:    and a2, a2, a3
; CHECK-NEXT:    and a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = and i64 %a0, %a1
  %t1 = and i64 %t0, %a2
  %t2 = and i64 %t1, %a3
  ret i64 %t2
}

define i8 @test_reassoc_or_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_or_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    or a0, a0, a1
; CHECK-NEXT:    or a2, a2, a3
; CHECK-NEXT:    or a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = or i8 %a0, %a1
  %t1 = or i8 %t0, %a2
  %t2 = or i8 %t1, %a3
  ret i8 %t2
}

define i16 @test_reassoc_or_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_or_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    or a0, a0, a1
; CHECK-NEXT:    or a2, a2, a3
; CHECK-NEXT:    or a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = or i16 %a0, %a1
  %t1 = or i16 %t0, %a2
  %t2 = or i16 %t1, %a3
  ret i16 %t2
}

define i32 @test_reassoc_or_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_or_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    or a0, a0, a1
; CHECK-NEXT:    or a2, a2, a3
; CHECK-NEXT:    or a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = or i32 %a0, %a1
  %t1 = or i32 %t0, %a2
  %t2 = or i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_or_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_or_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    or a0, a0, a1
; CHECK-NEXT:    or a2, a2, a3
; CHECK-NEXT:    or a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = or i64 %a0, %a1
  %t1 = or i64 %t0, %a2
  %t2 = or i64 %t1, %a3
  ret i64 %t2
}

define i8 @test_reassoc_xor_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_xor_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    xor a0, a0, a1
; CHECK-NEXT:    xor a2, a2, a3
; CHECK-NEXT:    xor a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = xor i8 %a0, %a1
  %t1 = xor i8 %t0, %a2
  %t2 = xor i8 %t1, %a3
  ret i8 %t2
}

define i16 @test_reassoc_xor_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_xor_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    xor a0, a0, a1
; CHECK-NEXT:    xor a2, a2, a3
; CHECK-NEXT:    xor a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = xor i16 %a0, %a1
  %t1 = xor i16 %t0, %a2
  %t2 = xor i16 %t1, %a3
  ret i16 %t2
}

define i32 @test_reassoc_xor_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_xor_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    xor a0, a0, a1
; CHECK-NEXT:    xor a2, a2, a3
; CHECK-NEXT:    xor a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = xor i32 %a0, %a1
  %t1 = xor i32 %t0, %a2
  %t2 = xor i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_xor_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_xor_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    xor a0, a0, a1
; CHECK-NEXT:    xor a2, a2, a3
; CHECK-NEXT:    xor a0, a0, a2
; CHECK-NEXT:    ret
  %t0 = xor i64 %a0, %a1
  %t1 = xor i64 %t0, %a2
  %t2 = xor i64 %t1, %a3
  ret i64 %t2
}

define i8 @test_reassoc_mul_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_mul_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    mul a1, a2, a3
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = mul i8 %a0, %a1
  %t1 = mul i8 %t0, %a2
  %t2 = mul i8 %t1, %a3
  ret i8 %t2
}

define i16 @test_reassoc_mul_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_mul_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    mul a1, a2, a3
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = mul i16 %a0, %a1
  %t1 = mul i16 %t0, %a2
  %t2 = mul i16 %t1, %a3
  ret i16 %t2
}

define i32 @test_reassoc_mul_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_mul_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    mul a1, a2, a3
; CHECK-NEXT:    mulw a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = mul i32 %a0, %a1
  %t1 = mul i32 %t0, %a2
  %t2 = mul i32 %t1, %a3
  ret i32 %t2
}

define i64 @test_reassoc_mul_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_mul_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    mul a1, a2, a3
; CHECK-NEXT:    mul a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = mul i64 %a0, %a1
  %t1 = mul i64 %t0, %a2
  %t2 = mul i64 %t1, %a3
  ret i64 %t2
}

define i8 @test_reassoc_minu_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_minu_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    andi a3, a3, 255
; CHECK-NEXT:    andi a2, a2, 255
; CHECK-NEXT:    andi a1, a1, 255
; CHECK-NEXT:    andi a0, a0, 255
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    minu a1, a2, a3
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i8 @llvm.umin.i8(i8 %a0, i8 %a1)
  %t1 = call i8 @llvm.umin.i8(i8 %t0, i8 %a2)
  %t2 = call i8 @llvm.umin.i8(i8 %t1, i8 %a3)
  ret i8 %t2
}

define i16 @test_reassoc_minu_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_minu_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    zext.h a3, a3
; CHECK-NEXT:    zext.h a2, a2
; CHECK-NEXT:    zext.h a1, a1
; CHECK-NEXT:    zext.h a0, a0
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    minu a1, a2, a3
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i16 @llvm.umin.i16(i16 %a0, i16 %a1)
  %t1 = call i16 @llvm.umin.i16(i16 %t0, i16 %a2)
  %t2 = call i16 @llvm.umin.i16(i16 %t1, i16 %a3)
  ret i16 %t2
}

define i32 @test_reassoc_minu_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_minu_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.w a3, a3
; CHECK-NEXT:    sext.w a2, a2
; CHECK-NEXT:    sext.w a1, a1
; CHECK-NEXT:    sext.w a0, a0
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    minu a1, a2, a3
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i32 @llvm.umin.i32(i32 %a0, i32 %a1)
  %t1 = call i32 @llvm.umin.i32(i32 %t0, i32 %a2)
  %t2 = call i32 @llvm.umin.i32(i32 %t1, i32 %a3)
  ret i32 %t2
}

define i64 @test_reassoc_minu_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_minu_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    minu a1, a2, a3
; CHECK-NEXT:    minu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i64 @llvm.umin.i64(i64 %a0, i64 %a1)
  %t1 = call i64 @llvm.umin.i64(i64 %t0, i64 %a2)
  %t2 = call i64 @llvm.umin.i64(i64 %t1, i64 %a3)
  ret i64 %t2
}

define i8 @test_reassoc_min_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_min_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.b a3, a3
; CHECK-NEXT:    sext.b a2, a2
; CHECK-NEXT:    sext.b a1, a1
; CHECK-NEXT:    sext.b a0, a0
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    min a1, a2, a3
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i8 @llvm.smin.i8(i8 %a0, i8 %a1)
  %t1 = call i8 @llvm.smin.i8(i8 %t0, i8 %a2)
  %t2 = call i8 @llvm.smin.i8(i8 %t1, i8 %a3)
  ret i8 %t2
}

define i16 @test_reassoc_min_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_min_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.h a3, a3
; CHECK-NEXT:    sext.h a2, a2
; CHECK-NEXT:    sext.h a1, a1
; CHECK-NEXT:    sext.h a0, a0
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    min a1, a2, a3
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i16 @llvm.smin.i16(i16 %a0, i16 %a1)
  %t1 = call i16 @llvm.smin.i16(i16 %t0, i16 %a2)
  %t2 = call i16 @llvm.smin.i16(i16 %t1, i16 %a3)
  ret i16 %t2
}

define i32 @test_reassoc_min_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_min_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.w a3, a3
; CHECK-NEXT:    sext.w a2, a2
; CHECK-NEXT:    sext.w a1, a1
; CHECK-NEXT:    sext.w a0, a0
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    min a1, a2, a3
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i32 @llvm.smin.i32(i32 %a0, i32 %a1)
  %t1 = call i32 @llvm.smin.i32(i32 %t0, i32 %a2)
  %t2 = call i32 @llvm.smin.i32(i32 %t1, i32 %a3)
  ret i32 %t2
}

define i64 @test_reassoc_min_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_min_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    min a1, a2, a3
; CHECK-NEXT:    min a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i64 @llvm.smin.i64(i64 %a0, i64 %a1)
  %t1 = call i64 @llvm.smin.i64(i64 %t0, i64 %a2)
  %t2 = call i64 @llvm.smin.i64(i64 %t1, i64 %a3)
  ret i64 %t2
}

define i8 @test_reassoc_maxu_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_maxu_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    andi a3, a3, 255
; CHECK-NEXT:    andi a2, a2, 255
; CHECK-NEXT:    andi a1, a1, 255
; CHECK-NEXT:    andi a0, a0, 255
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    maxu a1, a2, a3
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i8 @llvm.umax.i8(i8 %a0, i8 %a1)
  %t1 = call i8 @llvm.umax.i8(i8 %t0, i8 %a2)
  %t2 = call i8 @llvm.umax.i8(i8 %t1, i8 %a3)
  ret i8 %t2
}

define i16 @test_reassoc_maxu_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_maxu_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    zext.h a3, a3
; CHECK-NEXT:    zext.h a2, a2
; CHECK-NEXT:    zext.h a1, a1
; CHECK-NEXT:    zext.h a0, a0
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    maxu a1, a2, a3
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i16 @llvm.umax.i16(i16 %a0, i16 %a1)
  %t1 = call i16 @llvm.umax.i16(i16 %t0, i16 %a2)
  %t2 = call i16 @llvm.umax.i16(i16 %t1, i16 %a3)
  ret i16 %t2
}

define i32 @test_reassoc_maxu_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_maxu_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.w a3, a3
; CHECK-NEXT:    sext.w a2, a2
; CHECK-NEXT:    sext.w a1, a1
; CHECK-NEXT:    sext.w a0, a0
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    maxu a1, a2, a3
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i32 @llvm.umax.i32(i32 %a0, i32 %a1)
  %t1 = call i32 @llvm.umax.i32(i32 %t0, i32 %a2)
  %t2 = call i32 @llvm.umax.i32(i32 %t1, i32 %a3)
  ret i32 %t2
}

define i64 @test_reassoc_maxu_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_maxu_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    maxu a1, a2, a3
; CHECK-NEXT:    maxu a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i64 @llvm.umax.i64(i64 %a0, i64 %a1)
  %t1 = call i64 @llvm.umax.i64(i64 %t0, i64 %a2)
  %t2 = call i64 @llvm.umax.i64(i64 %t1, i64 %a3)
  ret i64 %t2
}

define i8 @test_reassoc_max_i8(i8 %a0, i8 %a1, i8 %a2, i8 %a3) {
; CHECK-LABEL: test_reassoc_max_i8:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.b a3, a3
; CHECK-NEXT:    sext.b a2, a2
; CHECK-NEXT:    sext.b a1, a1
; CHECK-NEXT:    sext.b a0, a0
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    max a1, a2, a3
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i8 @llvm.smax.i8(i8 %a0, i8 %a1)
  %t1 = call i8 @llvm.smax.i8(i8 %t0, i8 %a2)
  %t2 = call i8 @llvm.smax.i8(i8 %t1, i8 %a3)
  ret i8 %t2
}

define i16 @test_reassoc_max_i16(i16 %a0, i16 %a1, i16 %a2, i16 %a3) {
; CHECK-LABEL: test_reassoc_max_i16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.h a3, a3
; CHECK-NEXT:    sext.h a2, a2
; CHECK-NEXT:    sext.h a1, a1
; CHECK-NEXT:    sext.h a0, a0
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    max a1, a2, a3
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i16 @llvm.smax.i16(i16 %a0, i16 %a1)
  %t1 = call i16 @llvm.smax.i16(i16 %t0, i16 %a2)
  %t2 = call i16 @llvm.smax.i16(i16 %t1, i16 %a3)
  ret i16 %t2
}

define i32 @test_reassoc_max_i32(i32 %a0, i32 %a1, i32 %a2, i32 %a3) {
; CHECK-LABEL: test_reassoc_max_i32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    sext.w a3, a3
; CHECK-NEXT:    sext.w a2, a2
; CHECK-NEXT:    sext.w a1, a1
; CHECK-NEXT:    sext.w a0, a0
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    max a1, a2, a3
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i32 @llvm.smax.i32(i32 %a0, i32 %a1)
  %t1 = call i32 @llvm.smax.i32(i32 %t0, i32 %a2)
  %t2 = call i32 @llvm.smax.i32(i32 %t1, i32 %a3)
  ret i32 %t2
}

define i64 @test_reassoc_max_i64(i64 %a0, i64 %a1, i64 %a2, i64 %a3) {
; CHECK-LABEL: test_reassoc_max_i64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    max a1, a2, a3
; CHECK-NEXT:    max a0, a0, a1
; CHECK-NEXT:    ret
  %t0 = call i64 @llvm.smax.i64(i64 %a0, i64 %a1)
  %t1 = call i64 @llvm.smax.i64(i64 %t0, i64 %a2)
  %t2 = call i64 @llvm.smax.i64(i64 %t1, i64 %a3)
  ret i64 %t2
}

define half @test_fmin_f16(half %a0, half %a1, half %a2, half %a3) {
; CHECK-LABEL: test_fmin_f16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmin.h fa5, fa0, fa1
; CHECK-NEXT:    fmin.h fa4, fa2, fa3
; CHECK-NEXT:    fmin.h fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call half @llvm.minnum.f16(half %a0, half %a1)
  %t1 = call half @llvm.minnum.f16(half %t0, half %a2)
  %t2 = call half @llvm.minnum.f16(half %t1, half %a3)
  ret half %t2
}

define float @test_fmin_f32(float %a0, float %a1, float %a2, float %a3) {
; CHECK-LABEL: test_fmin_f32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmin.s fa5, fa0, fa1
; CHECK-NEXT:    fmin.s fa4, fa2, fa3
; CHECK-NEXT:    fmin.s fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call float @llvm.minnum.f32(float %a0, float %a1)
  %t1 = call float @llvm.minnum.f32(float %t0, float %a2)
  %t2 = call float @llvm.minnum.f32(float %t1, float %a3)
  ret float %t2
}

define double @test_fmin_f64(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_fmin_f64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmin.d fa5, fa0, fa1
; CHECK-NEXT:    fmin.d fa4, fa2, fa3
; CHECK-NEXT:    fmin.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call double @llvm.minnum.f64(double %a0, double %a1)
  %t1 = call double @llvm.minnum.f64(double %t0, double %a2)
  %t2 = call double @llvm.minnum.f64(double %t1, double %a3)
  ret double %t2
}

define half @test_fmax_f16(half %a0, half %a1, half %a2, half %a3) {
; CHECK-LABEL: test_fmax_f16:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmax.h fa5, fa0, fa1
; CHECK-NEXT:    fmax.h fa4, fa2, fa3
; CHECK-NEXT:    fmax.h fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call half @llvm.maxnum.f16(half %a0, half %a1)
  %t1 = call half @llvm.maxnum.f16(half %t0, half %a2)
  %t2 = call half @llvm.maxnum.f16(half %t1, half %a3)
  ret half %t2
}

define float @test_fmax_f32(float %a0, float %a1, float %a2, float %a3) {
; CHECK-LABEL: test_fmax_f32:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmax.s fa5, fa0, fa1
; CHECK-NEXT:    fmax.s fa4, fa2, fa3
; CHECK-NEXT:    fmax.s fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call float @llvm.maxnum.f32(float %a0, float %a1)
  %t1 = call float @llvm.maxnum.f32(float %t0, float %a2)
  %t2 = call float @llvm.maxnum.f32(float %t1, float %a3)
  ret float %t2
}

define double @test_fmax_f64(double %a0, double %a1, double %a2, double %a3) {
; CHECK-LABEL: test_fmax_f64:
; CHECK:       # %bb.0:
; CHECK-NEXT:    fmax.d fa5, fa0, fa1
; CHECK-NEXT:    fmax.d fa4, fa2, fa3
; CHECK-NEXT:    fmax.d fa0, fa5, fa4
; CHECK-NEXT:    ret
  %t0 = call double @llvm.maxnum.f64(double %a0, double %a1)
  %t1 = call double @llvm.maxnum.f64(double %t0, double %a2)
  %t2 = call double @llvm.maxnum.f64(double %t1, double %a3)
  ret double %t2
}

declare i8 @llvm.umin.i8(i8 %a, i8 %b)
declare i16 @llvm.umin.i16(i16 %a, i16 %b)
declare i32 @llvm.umin.i32(i32 %a, i32 %b)
declare i64 @llvm.umin.i64(i64 %a, i64 %b)
declare i8 @llvm.smin.i8(i8 %a, i8 %b)
declare i16 @llvm.smin.i16(i16 %a, i16 %b)
declare i32 @llvm.smin.i32(i32 %a, i32 %b)
declare i64 @llvm.smin.i64(i64 %a, i64 %b)
declare i8 @llvm.umax.i8(i8 %a, i8 %b)
declare i16 @llvm.umax.i16(i16 %a, i16 %b)
declare i32 @llvm.umax.i32(i32 %a, i32 %b)
declare i64 @llvm.umax.i64(i64 %a, i64 %b)
declare i8 @llvm.smax.i8(i8 %a, i8 %b)
declare i16 @llvm.smax.i16(i16 %a, i16 %b)
declare i32 @llvm.smax.i32(i32 %a, i32 %b)
declare i64 @llvm.smax.i64(i64 %a, i64 %b)
declare half @llvm.minnum.f16(half, half)
declare float @llvm.minnum.f32(float, float)
declare double @llvm.minnum.f64(double, double)
declare half @llvm.maxnum.f16(half, half)
declare float @llvm.maxnum.f32(float, float)
declare double @llvm.maxnum.f64(double, double)

define double @test_fmadd_strategy(double %a0, double %a1, double %a2, double %a3, i64 %flag) {
; CHECK_LOCAL-LABEL: test_fmadd_strategy:
; CHECK_LOCAL:       # %bb.0: # %entry
; CHECK_LOCAL-NEXT:    fmv.d fa5, fa0
; CHECK_LOCAL-NEXT:    fsub.d fa4, fa0, fa1
; CHECK_LOCAL-NEXT:    fmul.d fa0, fa4, fa2
; CHECK_LOCAL-NEXT:    andi a0, a0, 1
; CHECK_LOCAL-NEXT:    beqz a0, .LBB76_2
; CHECK_LOCAL-NEXT:  # %bb.1: # %entry
; CHECK_LOCAL-NEXT:    fmul.d fa4, fa5, fa1
; CHECK_LOCAL-NEXT:    fmadd.d fa5, fa5, fa1, fa0
; CHECK_LOCAL-NEXT:    fsub.d fa0, fa5, fa4
; CHECK_LOCAL-NEXT:  .LBB76_2: # %entry
; CHECK_LOCAL-NEXT:    ret
;
; CHECK_GLOBAL-LABEL: test_fmadd_strategy:
; CHECK_GLOBAL:       # %bb.0: # %entry
; CHECK_GLOBAL-NEXT:    fmv.d fa5, fa0
; CHECK_GLOBAL-NEXT:    fsub.d fa4, fa0, fa1
; CHECK_GLOBAL-NEXT:    fmul.d fa0, fa4, fa2
; CHECK_GLOBAL-NEXT:    andi a0, a0, 1
; CHECK_GLOBAL-NEXT:    beqz a0, .LBB76_2
; CHECK_GLOBAL-NEXT:  # %bb.1: # %entry
; CHECK_GLOBAL-NEXT:    fmul.d fa5, fa5, fa1
; CHECK_GLOBAL-NEXT:    fadd.d fa4, fa5, fa0
; CHECK_GLOBAL-NEXT:    fsub.d fa0, fa4, fa5
; CHECK_GLOBAL-NEXT:  .LBB76_2: # %entry
; CHECK_GLOBAL-NEXT:    ret
entry:
  %sub = fsub contract double %a0, %a1
  %mul = fmul contract double %sub, %a2
  %and = and i64 %flag, 1
  %tobool.not = icmp eq i64 %and, 0
  %mul2 = fmul contract double %a0, %a1
  %add = fadd contract double %mul2, %mul
  %sub3 = fsub contract double %add, %mul2
  %retval.0 = select i1 %tobool.not, double %mul, double %sub3
  ret double %retval.0
}
